import Foundation

protocol MPConvertibleNumber {}
protocol MPConvertibleDecimal {}
extension NSNumber: MPConvertibleNumber {}
extension Decimal: MPConvertibleNumber, MPConvertibleDecimal {}
extension NSDecimalNumber: MPConvertibleDecimal {}

protocol MPConvertibleDigitValue: MPConvertibleNumber {
    init?(truncating: NSNumber)
}

protocol MPConvertibleFloatValue: MPConvertibleDigitValue { }
extension Double: MPConvertibleFloatValue {}
extension Float: MPConvertibleFloatValue {}
extension CGFloat: MPConvertibleFloatValue {}

extension Bool: MPConvertibleDigitValue {}

protocol MPConvertibleIntValue: MPConvertibleDigitValue { }
extension Int: MPConvertibleIntValue {}
extension Int64: MPConvertibleIntValue {}
extension Int32: MPConvertibleIntValue {}
extension Int16: MPConvertibleIntValue {}
extension Int8: MPConvertibleIntValue {}
extension UInt: MPConvertibleIntValue {}
extension UInt64: MPConvertibleIntValue {}
extension UInt32: MPConvertibleIntValue {}
extension UInt16: MPConvertibleIntValue {}
extension UInt8: MPConvertibleIntValue {}

func mp_number(_ value: Any, type: Any.Type) -> MPConvertibleNumber? {
    guard let str = mp_numberString(value) else { return nil }
    guard let decimal = Decimal(string: str) else { return nil }
    if let digitType = type as? MPConvertibleDigitValue.Type {
        return Double("\(decimal)").flatMap { NSNumber(value: $0) }.flatMap { digitType.init(truncating: $0) }
    }
    if type is NSDecimalNumber.Type {
        return NSDecimalNumber(decimal: decimal)
    }
    if type is Decimal.Type { return decimal }
    return Double("\(decimal)").flatMap { NSNumber(value: $0) }
}

private func mp_numberString(_ value: Any) -> String? {
    if let date = value as? Date {
        return "\(date.timeIntervalSince1970)"
    }
    let lower = "\(value)".lowercased()
    if lower == "true" || lower == "yes" { return "1" }
    if lower == "false" || lower == "no" { return "0" }
    return lower
}
